#include "OpenGLRenderer.h"
#include "IMeshBuffer.h"
#include "IFace.h"
#include "ModelUtil.h"
#include "IMaterial.h"
#include "GeomPrimitives.h"
#include "..\..\..\Firebox\codebase\include\PhysicalProperty.h"
#include "..\..\..\Firebox\codebase\include\IContext.h"
#include "..\..\..\Firebox\codebase\include\World.h"

float rot = 0.f;

namespace steam
{

	namespace moon
	{

		OpenGLRenderer::OpenGLRenderer() : m_idCounter(0)
		{

		}

		void OpenGLRenderer::render()
		{
			glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
			glLoadIdentity();

			m_camera->update();

			glPushMatrix();
			base::Vector3 pos = m_camera->getPosition();
			glTranslatef(pos.x, pos.y, pos.z);
			m_skyBox.render();
			glPopMatrix();

			rot += 0.5f;
			if (rot > 360)
				rot -= 360;

			for (RenderInfoMap::const_iterator cit = m_renderInfoMap.begin(); cit != m_renderInfoMap.end(); ++cit)
			{
				const RenderInfo& rInfo = (*cit).second;
				if (rInfo.IsVisible)
				{
					glPushMatrix();
					glCallList(rInfo.RenderId);
					glPopMatrix();
				}
			}

			for (std::vector<wheel::IBox*>::const_iterator cit = m_boxes.begin(); cit != m_boxes.end(); ++cit)
			{
				const wheel::IBox* box = *cit;
				glPushMatrix();
				base::Matrix4 mat;
				box->getTransform(mat);
				glMultMatrixf(mat.get());
				base::Vector3 size = box->getInfo().Size;
				moon::GeomPrimitives::drawBox(size);
				glPopMatrix();
			}

			Entities m_entities = firebox::getContext().getWorld().getEntities();
			for (Entities::iterator it = m_entities.begin(), itE = m_entities.end(); it != itE; it++)
			{
				firebox::Entity* entity = *it;
				firebox::PhysicalProperty* phyProp = entity->getProperty<firebox::PhysicalProperty>();
				const base::Matrix4& mat = phyProp->getTransformation();
				glPushMatrix();
				//glMultMatrixf(mat.get());
				glTranslatef(mat.getPosition().x, mat.getPosition().y, mat.getPosition().z );
				std::cout<<mat.getPosition().x<< " , "<<mat.getPosition().y<< " , "<<mat.getPosition().z <<std::endl;
				base::Vector3 size(2,2,2);
				moon::GeomPrimitives::drawBox(size);
				glPopMatrix();
			}

		}

		unsigned int OpenGLRenderer::createRenderID(const coal::IMesh* mesh)
		{
			int meshBufferCount = mesh->getMeshBufferCount();

			if (meshBufferCount == 0)
			{
				return NULL;
			}

			unsigned int RenderId = glGenLists(1);
			glNewList(RenderId, GL_COMPILE);

			for (int i = 0; i < meshBufferCount; ++i)
			{
				bool hasTexture = false;
				const coal::IMeshBuffer* meshBuffer = mesh->getMeshBuffer(i);
				const coal::IMaterial* material = mesh->getMaterialByName(meshBuffer->getMaterialName());
				if (material && !material->getDiffuseTexture().empty())
				{
					glEnable(GL_TEXTURE_2D);
					unsigned int texID = SOIL_load_OGL_texture (
						material->getDiffuseTexture().c_str(),
						SOIL_LOAD_AUTO,
						SOIL_CREATE_NEW_ID,
						SOIL_FLAG_MIPMAPS | SOIL_FLAG_INVERT_Y | SOIL_FLAG_NTSC_SAFE_RGB | SOIL_FLAG_COMPRESS_TO_DXT
						);
					glBindTexture(GL_TEXTURE_2D, texID);
					hasTexture = true;
				}

				for (coal::ConstFaceIterator it = meshBuffer->getFaceStartIter(); it != meshBuffer->getFaceEndIter(); ++it)
				{
					const coal::IFace* face = *it;

					if (face->getFaceType() == coal::TRIANLGE_FACE)
					{		
						glBegin(GL_TRIANGLES);

						coal::IndexVector& vertexIndices = coal::ModelUtil::getVertexIndices(face);
						coal::IndexVector& texCoordIndices = coal::ModelUtil::getTexCoordIndices(face);

						for (unsigned int a = 0; a < vertexIndices.size(); a++)
						{
							const coal::Vertex& v = mesh->getVertices()[vertexIndices[a]];
							
							if (hasTexture)
							{
								const coal::TexCoord& t = mesh->getTexCoords()[texCoordIndices[a]];
								glTexCoord2f(t.s, t.t);
							}
							
							glVertex3f(v.x, v.y, v.z);
						}

						glEnd();
					}
				}

				if (hasTexture)
				{
					glDisable(GL_TEXTURE_2D);
				}
			}

			glEndList();

			return RenderId;
		}

		unsigned int OpenGLRenderer::createAndRegisterMesh( const coal::IMesh* mesh )
		{
			RenderInfo rInfo;
			rInfo.RenderId = createRenderID(mesh);
			rInfo.IsVisible = true;

			unsigned int uniqueId = ++m_idCounter; //base::IDGenerator::getUniqueID();
			m_renderInfoMap[uniqueId] = rInfo;
			return uniqueId;
		}

		SkyBox& OpenGLRenderer::getSkyBox()
		{
			return m_skyBox;
		}

		void OpenGLRenderer::setVisible( unsigned int id, bool visible )
		{
			RenderInfoMap::iterator findIter = m_renderInfoMap.find(id);
			if (findIter != m_renderInfoMap.end())
			{
				RenderInfo& rInfo = findIter->second;
				rInfo.IsVisible = visible;
			}
		}

		void OpenGLRenderer::setTerrain(ITerrain* terrain)
		{
			m_terrain = terrain;
		}

		ITerrain* OpenGLRenderer::getTerrain()
		{
			return m_terrain;
		}

		void OpenGLRenderer::setCamera( ICamera* camera )
		{
			m_camera = camera;
		}

		ICamera* OpenGLRenderer::getCamera()
		{
			return m_camera;
		}

		void OpenGLRenderer::registerBox( wheel::IBox* box )
		{
			m_boxes.push_back(box);
		}

	}
}